(function() {
	var gs = {
		//current hash path
		path: null,
		//cache all object
		keys: {},
		//places
		places: [],
		//page show max file item
		maxPos: 50,
		//real file path
		storagePath: 'https://sandbox.google.com/storage/',
		shareLink: 'http://commondatastorage.googleapis.com/',
		//need force reload
		reload: false,
		//check key is folder
		emptyFolder: new RegExp('_\\$folder\\$$'),

		//provide new actions and events
		actions: $('li.rename, li.copy, li.move, li.delete-files'),
		actionsEvent: {
			checkBox: null,
			checked: null,
			current: null,
			panel: null,
			newName: null,
			show: function() {
				var actionBg = $('div.action-bg');
				var newName = gs.actionsEvent.newName = (gs.actionsEvent.panel = actionBg.fadeIn().find('.action-fg div').hide().filter('.' + (gs.actionsEvent.current = this.className))).show().find('input:first').focus();
				actionBg.end().find('input[name=bucket]').val(gs.path);
				
				var ul = $('ul.target').empty(), name, href, td;
				gs.actionsEvent.checked = [];
				gs.actionsEvent.checkBox = $('table.file-list:visible tbody :checked').each(function() {
					td = $(this).parent().next();
					name = td.text().trim();
					href = td.children('a').attr('href');
					gs.actionsEvent.checked.push(td.is('.folder-icon') ? decodeURI(href.substr(1)) + '_$folder$' : decodeURI(href.substr(gs.storagePath.length)));
					ul.append('<li>%s</li>'.replace(/%s/,name));
					if(gs.actionsEvent.current == 'rename') return false;
				});
				if(gs.actionsEvent.current == 'rename')
					newName.val(function() {
						return name.indexOf('.') > -1 && !td.is('.folder-icon') ? '.' + name.split('.').pop() : '';
					})[0].setSelectionRange(0,0);
				var multi = gs.actionsEvent.checked.length > 1;
				actionBg.end().find('input.default').attr('checked', !multi).attr('disabled', multi);

				//if(gs.actionsEvent.checked.length == 1) actionBg.end().find('input[name='+this.className+']').val(gs.actionsEvent.checked[0].replace(gs.emptyFolder,''));
			},
			updateCache: function(path, checked, checkbox, name) {
				var keys = gs.keys[path], bucket = path.split('/')[0];
				var a = checkbox.parent().next().children('a'), newName;
				$.each(keys,function(i,key) {
					if(checked.match(gs.emptyFolder)) {
						keys = null;
						gs.keys[path] = gs.keys[ path +'/'+ name] = gs.reload;
						a.attr('href','#' + path +'/'+ name);
						a.text(name);
						return false;
					} else if(checked == bucket + '/'+ key[0]) {
						keys[i][0] = decodeURI(gs.getKeyName(a.attr('href').substr(gs.storagePath.length), 1, 1) + name);
						newName = bucket + '/' + keys[i][0];
						a.attr('href',gs.storagePath + newName);
						a.parent().parent().find('a.share-link').attr('href',gs.shareLink + newName).end().find('span.hidden').text(keys[i][0]);
						a.text(name);
						return false;
					}
				});				
			},
			selectedPath: function(path) {
				if(gs.actionsEvent.panel && gs.actionsEvent.panel.is(':visible'))
					gs.actionsEvent.panel.find(':text:eq(0)').val(path);

				var  upload = $('ol.folder-actions > li.upload-files');
				path ? upload.removeClass('disabled').find('div').removeClass('hidden') : upload.addClass('disabled').find('div').addClass('hidden');
			},

			rename: function(checked, checkbox, name, curr) {
				$.post('/storage/rename', {path: curr, key: checked[0], name: name}, function() {
					gs.actionsEvent.updateCache(curr, checked[0], checkbox.eq(0), name);
				});
			},
			copy: function(checked, checkbox, name, curr) {
				$.post('/storage/copy', {path: curr, keys: checked, name: name}, function() {
					gs.keys[name] = gs.reload;
				});
			},
			move: function(checked, checkbox, name, curr) {
				$.post('/storage/move', {path: curr, keys: checked, name: name}, function() {
					checkbox.parent().parent().remove();
					gs.keys[curr] = gs.keys[name] = gs.reload;
				});
			},
			'upload-files': function(event) {
				var i = 0, files = this.files, len = files.length, file, lock = false, err = '', limit = 10485760,
						monitor = $('#upload-monitor'), currName = monitor.find('.upload-file-name'),
						currCount = monitor.find('.upload-file-count'), bar = monitor.find('.progress-bar-inner'),
						tbody = $('table.file-table tbody'),now = new Date().getTime();
				if(len < 1) return;
				monitor.show();

				var timeId = setInterval(function () {
					if(lock) return;
					if(i >= len) {
						monitor.hide();
						bar.animate({'width' : '0'});
						return clearInterval(timeId);
					}

					file = files[i++];
					currCount.text('('+ i +' / '+ len +')');
					currName.text(file.name);
					bar.css('width', '0');
					if(file.size > limit)  {
						err += file.name + ' file size limit ' + gs.getFileSize(limit).join(' ') + '<br/>';
						$('div.error').html(err).fadeIn();
						return;
					}
					var xhr = new XMLHttpRequest();
					xhr.open("POST", "/storage/upload", true);
					xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary=boundary-103973");
					xhr.onreadystatechange = function() {
						if(xhr.readyState ==4) {
							if(xhr.status == 200) {
								lock = false;
								gs.newFile(tbody, [file.name, file.size , now, false]);
							} else {
								lock = false;
								err += xhr.responseText + " (status code = " + xhr.status + ')<br/>';
								$('div.error').html(err).fadeIn();
							}
						}
					};
					xhr.upload.addEventListener("progress", function(e) {
						if(e.lengthComputable) {
							var percentComplete = parseInt((e.position / e.totalSize)*100) + "%";
							bar.animate({'width' : percentComplete});
						}
					}, false);

					xhr.setRequestHeader('UP-GS-PATH', gs.path);
					xhr.setRequestHeader('UP-FILENAME', file.name);
					xhr.setRequestHeader('UP-SIZE', file.size);
					xhr.setRequestHeader('UP-TYPE', file.type);
					lock = true;
					xhr.send(file);
					/*var reader = new FileReader();
					reader.readAsBinaryString(file);
					reader.onload = function(ex) {};*/
				}, 500);
			},
			'delete-files': function(checked, checkbox, name, curr) {
				$.post('/storage/delete', {path: curr, keys: checked, name: name}, function() {
					checkbox.parent().parent().remove();
					gs.keys[curr] = gs.keys[name] = gs.reload;
				});
			}
		},

		//initialize element event handle
		init: function() {
			//places-toggle-button
			var navLeft = $('#nav-left');
			$('.places-toggle-button').click(function() {
				$(this).parent().hide().next().show();
				navLeft.hide().next().hide();
				gs.navPlaces = false;
			});
			gs.navPlacesShow = $('.places-off').click(function() {
				$(this).hide().prev().show();
				navLeft.show().next().show();
				gs.navPlaces = true;
			});

			//file-selected check all
			$('thead :checkbox').click(function() {
				$(this).closest('table').find('tbody :checkbox').attr('checked', this.checked).each(function(){
					var tr = $(this).closest('tr.file-container');
                    this.checked ? tr.addClass('selected') : tr.removeClass('selected');
				});
			});

			//new bucket
			$('.add-folder').click(function() {
                var bucket = $('table.file-list:visible tr.newBucket');
                if(bucket.length > 0) return bucket.show().find(':text').focus();

                bucket = $(gs.getNewBucketHTML('', {})).addClass('newBucket');
                bucket.find('td.file-name').html(gs.newBucketHTML2).end().find('td.file-selected').html('');
                $('table.file-list:visible').prepend(bucket).find(':text').focus().keyup(function(event) {
                    if (event.keyCode == 27) {this.value = '';gs.newBucket.apply(this, arguments);}
                    else if (event.keyCode == 13) gs.newBucket.apply(this, arguments);
                }).blur(gs.newBucket);
			});

            //add-to-places
            $('.add-to-places').click(function() {
				var places = $('ul.places-list'), li = places.children("li"), newPlaces = [];
                $('table.file-list:visible tbody :checked').parent().next('[draggable]').children('a').each(function(){//file-name
                    //var icon = gs.path ? (this.parentNode.getAttribute('draggable') ? 'folder-icon' : 'file-icon-photo') : 'bucket-icon';
					var icon = gs.path ? 'folder-icon' : 'bucket-icon';
                    var link = this.href.substr(this.href.indexOf('#') + 1), name = $(this).text(), title  = decodeURI(link);
					if(li.filter(function(){return this.title == title}).length == 0) {
                    	places.append(gs.placeHTML.replace(/%title/,title).replace(/%link/,link).replace(/%icon/,icon).replace(/%name/,name));
						newPlaces.push([title,link,name,icon]);
					}
                });
				if(newPlaces) gs.savePlaces(newPlaces);
            });

			//refresh
			$('.folder-actions .refresh-link').click(function() {
				$.post('/storage/clearBucket', {path: gs.path, retrieve: 'true'}, function(keys) {
					if(!gs.path) {
						var tbody = $('table.bucket-table tbody');
						tbody.empty();
						$.each(JSON.parse(keys), function(i, key) {
							tbody.append(gs.newBucketHTML.replace(/%name/g, key));
						});
					} else gs.createTable(gs.keys[gs.path] = JSON.parse(keys), gs.getPrefix(gs.path), null);
				});
			});

			//actions
			gs.actions.click(function() {
				if($(this).is('.disabled')) return;
				gs.actionsEvent['show'].apply(this, arguments);
			});
			gs.actionCancel = $('.action-bg > input[name=cancel]').click(function(){$(this).parent().fadeOut();});
			$('.action-bg > input[name=ok]').click(function() {
				var checked = gs.actionsEvent.checked;
				if(checked && checked.length < 1 ) return $('div.error').html('not selected item').fadeIn();
				
				var name, curr = gs.actionsEvent.panel.find(':text[name=bucket]').val();
				if(gs.actionsEvent.current != 'delete-files') {
					name = gs.actionsEvent.newName.val().trim();
					if(name == '') return $('div.error').html('entry new name').fadeIn();
					if(curr == name) return $('div.error').html('the same current path').fadeIn();
				}
				var r = gs.actionsEvent[gs.actionsEvent.current](checked, gs.actionsEvent.checkBox, name, curr);
				if(r !== false) gs.actionCancel.click();
			});

			//upload
			$('li.upload-files input[type=file]').change(gs.actionsEvent['upload-files']);

            //delete places
            $('div.delete-icon-dark').live('click',function(){
                var li = $(this).parent(), title = li.attr('title');
                $.each(gs.places, function(i, place) {
                    if(place[0] == title) {
                        gs.places.splice(i,1);
                        gs.savePlaces();
                        li.remove();
                        return false;
                    }
                });
            });

            $('td.file-selected :checkbox').live('click', gs.hasChecked);
			$('tbody td.file-share div.check-icon').live('click', gs.makePublic);

			gs.searchBox = $(':text.search-box').keyup(function(event) {
				if(event.keyCode == 13) {
					gs.createTable(gs.keys[gs.path], gs.getPrefix(gs.path), $(this).val().trim().toLowerCase());
				} else if(event.keyCode == 27) {
					$(this).val('');
					gs.createTable(gs.keys[gs.path], gs.getPrefix(gs.path), null);
				}
			});

			gs.loadMore = $('div.load-more').click(function() {
				var tbody = gs.loadMore.data('tbody'), files = gs.loadMore.data('files');
				if(!tbody || !files || files.length == 0) return gs.loadMore.fadeOut() && false;
				
				var all = tbody.data('all'), show = files.splice(0, gs.maxPos), name, acl = [];
				$.each(show, function(i, file) {
					name = gs.newFile(tbody, file);
					if(file[3] === undefined) acl.push(file[0]);
				});

				(files.length > 0) ? gs.loadMore.fadeIn() : gs.loadMore.fadeOut();
				if (acl.length > 0) gs.getAcl(tbody, all - files.length - show.length + tbody.data('existFolder'), acl);
				gs.searchBox.next().text(all - files.length);
			});
			$('div.error').dblclick(function(){$(this).fadeOut();});
            //ajax error
            $('div.ajax-load').ajaxError(function(event,req) {
                $('div.ajax-load').hide();
				$('table.file-table thead td.file-share div').removeClass('loading-icon2');
				$('div.error').html(req.responseText).fadeIn();
            }).ajaxSend(function(e,req,setting){
	            if(['/storage/getAcl'].indexOf(setting.url) < 0)
					gs.ajaxLoadIcon = $('div.ajax-load').show();
            }).ajaxComplete(function(){
	            if(gs.ajaxLoadIcon) gs.ajaxLoadIcon.hide();
            });

			gs.loadPlaces();
		},

        //tools
        uri: function(path) {
            return encodeURI(decodeURI(path));
        },
        getNewBucketHTML: function(name, kwargs) {
            kwargs = kwargs ? kwargs : {};
            return (gs.path ? gs.newFolderHTML.replace(/%size/g, kwargs.size || '0 / 0') : gs.newBucketHTML).replace(/%name/g, name).replace(/%link/g, gs.uri(window.location.hash + '/' + name));
        },
        hasChecked: function(check) {
            if(!check) $('thead :checkbox').attr('checked', false);
	        else {
	            var tr = $(this).closest('tr.file-container');
                this.checked ? tr.addClass('selected') : tr.removeClass('selected');
            }
	        
            var hasChecked = !check ? false : $('table.file-list:visible tbody :checked').length > 0;
			if(gs.path) hasChecked ? gs.actions.removeClass('disabled') : gs.actions.addClass('disabled');
			else gs.actions.addClass('disabled');
			hasChecked ? $('li.add-to-places').removeClass('disabled') : $('li.add-to-places').addClass('disabled');
        },

		//create bucket
		newBucketHTML: '<tr class="file-container"><td class="file-selected"><input type="checkbox"></td><td draggable="true" class="file-name bucket-icon"><a href="#%name"><span> %name </span></a></td></tr>',
		newBucketHTML2: '<input type="text" value=""><span class="error-message"></span>',
        placeHTML: '<li title="%title"><a href="#%link"><h3 class="%icon">%name</h3></a><div class="menu delete-icon-dark" title="Remove this place"></div></li>',
		newBucket: function() {
			var text = $(this);
			if (gs.newBucket.lock) return;

			if (text.val() == '') {
				text.next().text('').parent().parent().hide();
			} else {
				gs.newBucket.lock = true;
				text.parent().prev().addClass('loading-icon');
				var newBucketName = text.val().trim(), isExist = false;
                $('table.file-list:visible tbody td.file-name').each(function() {
                    if($(this).text().trim() == newBucketName) return (isExist = true) && false;
                });
                if(isExist) return text.next().text('exist! name : "' + newBucketName + '"') && (gs.newBucket.lock = false);

				$.post('/storage/newBucket', {path: gs.path, name: newBucketName}, function(data) {
					if (data.indexOf('^1') == 0) {
						text.parent().parent().hide();
                        var bucket = $(gs.getNewBucketHTML(newBucketName, {}));
						text.val('').parent().parent().before(bucket);
                        gs.keys[gs.path + '/' +newBucketName] = {};
					} else {
						text.val('').next().text(data.substr(2)).end().focus();
					}
					text.parent().prev().removeClass('loading-icon');
					gs.newBucket.lock = false;
				});
			}
		},

		//create folder
		newFolderHTML: '<tr class="file-container"><td class="file-selected"><input type="checkbox" /></td><td draggable="true" class="file-name folder-icon"><a href="%link"><span>%name</span></a></td><td class="file-size folder-file-size">%size</td><td class="file-modified"></td><td class="file-share"></td></tr>',
		newFolder: function(name, size) {
			$('table.file-table tbody').append(gs.getNewBucketHTML(name, {size: size || name}));
		},

		//create file
		newFileHTML: '<tr class="file-container "><td class="file-selected"><input type="checkbox" /></td><td class="file-name file-icon-photo"><a target="_blank" href="https://sandbox.google.com/storage/%link">%name</a></td><td class="file-size">%size %unit</td><td class="file-modified">%modified</td><td class="file-share"><div class="check-icon check-%isShare"></div>%share</td></tr>',
        newFileHTMLShare0: '<span class="hidden">%link</span>',
        newFileHTMLShare: '<a href="http://commondatastorage.googleapis.com/%link" draggable="false" class="share-link" target="_blank">link</a>',
        unit: ['Byte','KB','MB','GB','TB'],
        getFileSize: function(size,level) {
            if(!level) level = 0;
            if(size >= 1024 && level < gs.unit.length - 1) return arguments.callee(size / 1024, ++level);
            return [Math.round(size * 100) / 100 , gs.unit[level]];
        },
		getFileDate: function(date) {
			date = new Date(date);
			if(!date) return date;
			return date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate() + " " + date.getHours() + ":" + date.getMinutes();
		},
		newFile: function(tbody, file) {
            var name = file[0].split('/').pop(), size =  gs.getFileSize(file[1],0), acl = file[3] != undefined, link = gs.uri(gs.path.split('/')[0] + '/' + file[0]);

            var html = gs.newFileHTML.replace(/%name/, name).replace(/%link/, link)
                    .replace(/%size/, size[0]).replace(/%unit/, size[1])
                    .replace(/%modified/, gs.getFileDate(file[2])).replace(/%isShare/,acl ? (file[3] ? 'on' : 'off') : 'none')
                    .replace(/%share/,((acl && file[3]) ? gs.newFileHTMLShare : gs.newFileHTMLShare0).replace(/%link/, (acl && file[3]) ? link : file[0])); //
			tbody.append(html);
			return name;
		},

		//navigation handle
		getPrefix: function(path) {
			var prefix = path.split('/');
			prefix = prefix.slice(1, prefix.length).join('/');
			return prefix ? prefix + '/' : prefix;  			
		},
		//default without bucket name
		getKeyName: function(path, startSplit, lastSplit) {
			if(startSplit === undefined) startSplit = 1;
			if(!lastSplit) lastSplit = 0;
			var ps = path.split('/');
			ps = ps.slice(startSplit, ps.length - lastSplit).join('/');
			return lastSplit && ps ? ps + '/' : ps;
		},
		getKeys: function(path, func) {
			var keys = gs.keys[path];
			if (keys) {
				func(keys);
			} else {
				if(keys !== gs.reload) {
					//reverse turn check parent
					var ps = path.split('/'), index = ps.length - 1;
					while(index > 0) {
						if(keys = gs.keys[ps.slice(0, index--).join('/')]) {
							func(keys);
							return true;
						}
					}
				}
				//remote load
				$.get('/storage/' + path + "/", function(keys) {
					func(gs.keys[path] = JSON.parse(keys));
				});
			}
		},
		breadcrumbHTML: '<li draggable="true" class="bucket-icon %selected"><a href="#%link">%name</a></li>',
		breadcrumb: function(path) {
			var home = $('ol.breadcrumb');
			home.children('li.selected').removeClass('selected');
			home.children(':gt(0)').remove();
            path ? $('.add-folder').removeClass('bucket-icon').addClass('folder-icon').attr('title','Add new folder').text('New Folder'):
                   $('.add-folder').removeClass('folder-icon').addClass('bucket-icon').attr('title','Add new bucket').text('New Bucket');
			if(!path) return home.children(':first').addClass('selected');
			var paths = path.split('/');
			$.each(paths,function(i, p) {
				home.append(gs.breadcrumbHTML.replace(/%selected/,((i == paths.length - 1) ? 'selected' : '')).replace(/%name/,p).replace(/%link/,gs.uri(paths.slice(0, i + 1).join('/'))));
			});
			$('table.file-table input.search-box').val('');
		},
		navigation: function(path) {
			gs.path = path;
            gs.hasChecked(false);
			gs.breadcrumb(path);
			gs.actionsEvent.selectedPath(path);
			if (path != '') {
                $('table.bucket-table').hide();

				gs.getKeys(path, function(keys) {
					var prefix = gs.getPrefix(path);
					gs.createTable(keys,prefix, null);
					$('table.file-table').show();
				});
			}
			else {
				gs.loadMore.fadeOut();
				$('table.file-table').hide();
				$('table.bucket-table').find(':checkbox').attr('checked', false).end().show();
			}
		},
		createTable: function(keys, prefix, search) {
			var tbody = $('table.file-table tbody');
			tbody.empty();
            if(!keys && keys != []) return;

			var existFolder = [], files = [], all = [], folderSize = {}, folderCount = {};
			var emptyFolder = gs.emptyFolder;
			$.each(keys, function(i, key) {
				if(key[0].indexOf(prefix) !=0 ) return true;
				var keyName = prefix ? key[0].replace(prefix,'') : key[0], firstFolder = keyName.indexOf('/');

				if(search) {
					if(keyName.toLowerCase().indexOf(search) > -1 && !keyName.match(emptyFolder))
						files.push(key);
					return true;
				}

				all.push(key);
                if(firstFolder < 0) {
                    if(keyName.match(emptyFolder)) {
						if(existFolder.indexOf(keyName.replace(emptyFolder,'')) < 0) {
							gs.newFolder(keyName.replace(emptyFolder,''), '0 / 0');
							existFolder.push(keyName);
						}
                    } else files.push(key);
                } else if(firstFolder > 0) {
					keyName = keyName.substr(0,firstFolder);
					if(existFolder.indexOf(keyName) >= 0) {
                        folderSize[keyName] = folderSize[keyName] + key[1];
                        folderCount[keyName] = folderCount[keyName] + 1;
						return true;
					}

                    folderSize[keyName] = key[1];
                    folderCount[keyName] = 1;
					gs.newFolder(keyName, null);
					existFolder.push(keyName);
				}
			});

            if(!search) {
				gs.keys[gs.path] = all;
				var folders = $('tr.file-container td.folder-file-size');
				for(var i in folderSize) {
					folders.each(function() {
						if(this.innerHTML == i)
							this.innerHTML = gs.getFileSize(folderSize[i]).join(' ') + ' / ' + folderCount[i];
					});
				}
			}
			
			gs.searchBox.next().next().text(files.length);
			tbody.data('all', files.length).data('existFolder', existFolder.length);
			gs.loadMore.data('tbody', tbody).data('files',files).click();
		},

		getAcl: function(tbody, startTd, keys) {
			tbody.prev().find('td.file-share div').addClass('loading-icon2');
			$.post('/storage/getAcl', {path: gs.path, keys: keys}, function(data) {
				var files = gs.keys[gs.path];
				data = $.parseJSON(data);
				var gt = --startTd < 0 ? '' : ':gt('+startTd+')';
				var isPublic, cls, clsPre = 'check-', span, text, shareTd = tbody.find('td.file-share%gt span'.replace(/%gt/,gt)), startPos = 0;
				shareTd.each(function() {
					span = $(this), text = decodeURI(span.text());
					isPublic = !!data[text];
					cls = isPublic ? 'on' : 'off';
					span.prev().removeClass(clsPre + 'none').addClass(clsPre + cls);
					if(isPublic) {
						span.after(gs.newFileHTMLShare.replace(/%link/, gs.uri(gs.path.split('/')[0] + '/' + text)));
					}

					gs.cacheAcl(startPos, files, text, isPublic);
				});

                //gs.keys[gs.path] = files;
				tbody.prev().find('td.file-share div').removeClass('loading-icon2');
			});
		},

		makePublic: function() {
			var div = $(this), name = div.closest('tr').find('td.file-name a').attr('href').substr(gs.storagePath.length), isPublic;

			$.post('/storage/makePublic',{name: name}, function(data){
				if(data.indexOf('^1') == 0) {
					if(isPublic = (data.substr(2).toLowerCase() == 'true')) {
						div.removeClass('check-off').addClass('check-on');
						div.parent().append(gs.newFileHTMLShare.replace(/%link/, gs.uri(name)));
					} else {
						div.removeClass('check-on').addClass('check-off');
						div.parent().children().remove('a');
					}

					gs.cacheAcl(0, gs.keys[gs.path], gs.getKeyName(name), isPublic);
				}
			});
		},

		cacheAcl: function(startPos, files, text, isPublic) {
			var len = files.length;
			text = decodeURI(text);
			for(var i = startPos; i < len; i++) {
				if(files[i][0] == text) {
					files[i][3] = isPublic;
					startPos = i;					
					break;
				}
			}
		},

		savePlaces: function(places) {
			if(places) gs.places = gs.places.concat(places);
			localStorage.setItem(gs.tryId + 'places',JSON.stringify(gs.places));
		},

		loadPlaces: function() {
			if(gs.navPlaces = localStorage[gs.tryId + 'navPlaces'] != 'false')
				gs.navPlacesShow.click();
			//[title,link,name,icon]
			var items = localStorage.getItem(gs.tryId + 'places');
			if(items) {
				gs.places = items = window.JSON.parse(items);
				var places = $('ul.places-list');
				$.each(items, function(i, place) {
					places.append(gs.placeHTML.replace(/%title/,place[0]).replace(/%link/,place[1]).replace(/%icon/,place[3]).replace(/%name/,place[2]));
				});
			}
		}
	};
	
	//on load
	$(function() {
		$('#login-form').submit(function() {
			if($(this).find(":text[value='']").length > 0)
				return $('div.error').html('id or key is empty').fadeIn() && false;
			return true;
		});
		if((gs.tryId = $('#try_id').text().trim()) == '') return;
		
		var endSlash = /\/+$/;
		//on navigation
		window.onhashchange = function() {
			gs.navigation(decodeURI(window.location.hash.replace(endSlash, '').substr(1)));
		};

		window.onbeforeunload = function() {
			localStorage[gs.tryId + 'keys'] = JSON.stringify(gs.keys);
			localStorage[gs.tryId + 'navPlaces'] = gs.navPlaces;
		};
		
		gs.keys = JSON.parse(localStorage[gs.tryId + 'keys'] || '{}');

		//start here
		gs.init();
		
		if (window.location.hash == '' && !window.location.href.match(/\/#$/)) window.location.href = '#';
        else  window.onhashchange();

	});
})();
